#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<errno.h>
#include<sys/stat.h>
#include<fcntl.h>


char stringArg[1024]; //获取命令行输入
char* argv[32]; //拆解后的命令行参数数组
#define CHANGEOUT 1  //输出重定向
#define CHANGEIN 2  //输入重定向
#define CHANGEAPPEND 4  //追加重定向
char*filename; //重定向的目标文件
int MODE;  //记录是否重定向


int main()
{
    stringArg[1023]=0;
    //printf("%s\n",getenv("HOST"));

    while(1)
    {  
        //重置errno
        errno=0;
        MODE =0 ;

        //获取字符串提取命令行

        memset(stringArg,0,1024); //全部初始化\0 ，便于计算长度
        memset(argv,0,32*sizeof(char*));
        //打印一行提示信息   用户名@主机名 当前路径$
        printf("[%s@ %s %s]$ ",getenv("USER"),getenv("HOSTNAME"),getenv("PWD"));   //不换行
        fflush(stdout);   //将提示信息打印出来
        //获取字符串   要读空格，不能用 scanf ，使用fgets
        fgets(stringArg,1023,stdin);
        stringArg[strlen(stringArg)-1]=0; //将最后的 \n 换成 \0 
        if(strlen(stringArg)==0)
        {
            //什么也没输入
            continue;
        }
        
        //判断是否有重定向
        int j=0;
        for(j=0;j<strlen(stringArg);++j)
        {
            if(stringArg[j]=='<')
            {
                //输入重定向
                MODE=CHANGEIN;
                stringArg[j]='\0';
                filename=&stringArg[j+1];
                break;
            }
            else if(stringArg[j]=='>')
            {
                stringArg[j]='\0';
                if(stringArg[j+1]=='>')
                {
                    j++;
                    MODE=CHANGEAPPEND;
                    stringArg[j]='\0';
                    filename=&stringArg[j+1];
                    break;
                }
                else
                {
                    MODE=CHANGEOUT;
                    filename=&stringArg[j+1];
                    break;
                }
            }
        }

        //拆解字符串 strtok
        int i=0;
        argv[i++]=strtok(stringArg," ");
        if(strcmp("echo",argv[0])==0&&MODE==0) //echo
        {
            //检查$
            if(stringArg[5]=='$')
            {
                //只考虑环境变量
                printf("%s\n",getenv(&stringArg[6]));
            }
            else
                printf("%s\n",&stringArg[5]);
            continue;
        }
        while(argv[i++]=strtok(NULL," ")); //最后一次当字符串结束再使用strtok，会返回NULL,刚好作为结束条件以及命令行参数数组的结尾NULL
       

        //内建命令
        if(strcmp("cd",argv[0])==0)  //cd
        {
            const char*changepath=argv[1];
            int ret=chdir(changepath);
            if(ret==-1)
            {
                printf("%s\n",strerror(errno));
            }
            continue; 
            
        }

        //程序替换
        else
        {
            pid_t id=fork();
         
             if(id<0)
             {
                 perror("fork failed");
                 exit(1);
             }
             if(id==0)
             {
            //先检查是否有重定向
            if(MODE==CHANGEOUT)
            {
                int fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
                dup2(fd,1);
            }
            if(MODE==CHANGEIN)
            {
                int fd=open(filename,O_RDONLY);
                dup2(fd,0);
            }
            if(MODE==CHANGEAPPEND)
            {
                int fd = open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);
                dup2(fd,1);
            }
                 //程序替换
                 execvp(argv[0],argv);
                 //如果替换失败则会执行下面的代码
                 perror("execvp failed");
                 exit(1);
             }

             //父进程等待回收僵尸
             int status=0;
             waitpid(id,&status,0);
             if(WIFEXITED(status))//正常退出
             {
                 if(WEXITSTATUS(status))//退出码不为0 
                 {
                     printf("运行成功，退出码为 :%d\n",  WEXITSTATUS(status));
                 }
             }
             else//异常终止
             {
                 printf("%s\n",strerror(errno));
             }
        }


#ifdef _DEBUG_ARGV//测试切割功能
        char**p=argv;
        while(*p)
            printf("%s ",*p++);
        printf("\n");
#endif
    }


    return 0;
}




